Post

Replies

Boosts

Views

Activity

Reply to NSPopover not showing content on macOS Sonoma
I found the root cause. One of the popover view's subviews was my custom NSView which overrides drawRect method: - (void) drawRect:(NSRect)dirtyRect { NSLog(@"DIRTY RECT: %@", NSStringFromRect(dirtyRect)); [colorSeparator setFill]; NSRectFill(dirtyRect); [super drawRect:dirtyRect]; } but it gets weird rect as parameter: DIRTY RECT: {{-13, -56}, {426, 476}} DIRTY RECT: {{-13, -411}, {426, 476}} So I just removed that method and set the background color via layer: @implementation CDSeparatorView - (instancetype) init { self = [super init]; if (self) { [self setupInstance]; } return self; } - (instancetype) initWithFrame:(NSRect)frameRect { self = [super initWithFrame:frameRect]; if (self) { [self setupInstance]; } return self; } - (instancetype) initWithCoder:(NSCoder*)coder { self = [super initWithCoder:coder]; if (self) { [self setupInstance]; } return self; } - (void) setupInstance { self.translatesAutoresizingMaskIntoConstraints = NO; self.wantsLayer = YES; self.layer.backgroundColor = SREG.theme.colorSeparator.CGColor; } @end and now the app is drawing the content just fine.
Sep ’23
Reply to NSPopover not showing content on macOS Sonoma
Not yet, but simplifying the addChildViewController logic did show my inner container view but still work to be done to get it to work as before, I hope to fix it today/tomorrow. - (void) loadView { self.view = NSView.new; self.rootViewController.view.translatesAutoresizingMaskIntoConstraints = NO; [self addChildViewController:self.rootViewController]; [self.view addSubview:self.rootViewController.view]; id v = @{ @"v": self.rootViewController.view }; [self.view addFor:v constraints:@"|[v]|"]; [self.view addFor:v constraints:@"V:|[v]|"]; }
Sep ’23
Reply to How to add new NSFileProviderDecorations to FileProviderExtensions
What helped in the end was deleting all of derived data, products and caches from both Xcode and AppCode: Delete FileProvider Main App and Extension (~/Library/Containers/MyApp, ~/Library/Containers/MyAppExtension, ~/Library/Group Containers/MyApp) Delete Xcode Derived Data (Library/Developer/Xcode/DerivedData/MyApp) Delete AppCode Derived Data (Library/Caches/JetBrains/AppCode2023.1/DerivedData/MyApp) Restart macOS Open project in Xcode, build it and run This resulted in new decoration icons appearing properly.
Jul ’23
Reply to How to open macOS FileProvider CloudStorage folder programmatically
Quite simple actually: let domain = SREG.context.domain guard let fileProviderFolderURL = try! await NSFileProviderManager(for: domain)?.getUserVisibleURL(for: .rootContainer) else { print("No CloudStorage URL found") return } let securityScopeResult = fileProviderFolderURL.startAccessingSecurityScopedResource() if !securityScopeResult { L.error("Alias location URL security scope was not granted") showFinderInstructions = true return } let openResult = NSWorkspace.shared.open(fileProviderFolderURL) if !openResult { L.error("NSWorkspace failed to open file provider folder.") showFinderInstructions = true } fileProviderFolderURL.stopAccessingSecurityScopedResource()
Jul ’23
Reply to How to prevent writing to file in macOS FileProviderExtension
I did have some advancements by utilizing fileSystemFlags in NSFileProviderItem: var fileSystemFlags: NSFileProviderFileSystemFlags { let ret = NSFileProviderFileSystemFlags(rawValue: private_fileSystemFlags) if locked { return ret.subtracting(.userWritable) } else { return ret } } and indeed when opening ie a PNG file in Preview.app and trying to modify it, I get alert that file is locked. However, when user clicks on Unlock, modifyItem is invoked with fileSystemFlags changed, and that's fine because I can handle that callback and remove the userWriteable flag: if changedFields.contains(.fileSystemFlags) { var flags = item.fileSystemFlags! if node.nodeLocked() { flags.subtract(.userWritable) } node.fileSystemFlags = Int32(flags.rawValue) } however, Preview.app will again offer to Unlock the file and if user clicks on Unlock then, system will just write to the file modifying the fileSystemFlag without invocation of modifyItem, as if it happens under the hood without the FileProviderExtension's control or chance to do anything about that.
Jun ’23
Reply to How to access files in macOS FileProvider Extension programmatically?
The solution is quite simple: Task{ guard let fileProviderFolderURL = try! await NSFileProviderManager(for: domain)?.getUserVisibleURL(for: .rootContainer) else{ print("No CloudStorage URL found") return } fileProviderFolderURL.startAccessingSecurityScopedResource() let openResult = NSWorkspace.shared.open(fileProviderFolderURL) if !openResult { print("There was an error opening FileProvider Folder") } fileProviderFolderURL.stopAccessingSecurityScopedResource() }
May ’23
Reply to How to access files in macOS FileProvider Extension programmatically?
I was chaising my own tail for a while. Anyways, here's a solution that worked for me: Here is an example on how to open a FileProvider folder: Retrieve FileProvider root folder URL: let fileProviderFolderURL = try! await NSFileProviderManager(for: SREG.context.domain)?.getUserVisibleURL(for: .rootContainer) Use NSOpenPanel to ask user where to store the symbolic link to FileProvider root folder. Then store bookmark data of user-selected location and create symbolic link to FileProvider root within that location. let savePanel = NSOpenPanel() savePanel.canChooseDirectories = true savePanel.title = "Choose this location" savePanel.prompt = "Choose" let result = await savePanel.begin() if result == NSApplication.ModalResponse.OK { let panelURL = savePanel.url let mountBookmarkData = try! panelURL.bookmarkData(options: .withSecurityScope) UserDefaults.standard.set(mountBookmarkData, forKey: "aliasLocationBookmarkData") let fileManager = FileManager.default var aliasURL = panelURL.appendingPathComponent("MyRoot") UserDefaults.standard.set(aliasURL.path, forKey: Constants.UserDefaults.aliasPath) do { try fileManager.createSymbolicLink(at: aliasURL, withDestinationURL: fileProviderFolderURL) } catch { // handle error } } Later, when you want to open the Root folder in Finder, fetch bookmark data from UserDefaults and create URL from it. It is the location where symbolic link is stored. Start secure access on it and then open the symbolic link URL: let aliasLocationBookmarkData = UserDefaults.standard.data(forKey: "aliasBookmarkData")! var isStale = false let aliasLocationURL = try URL(resolvingBookmarkData: aliasLocationBookmarkData, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale) let securityScopeResult = aliasLocationURL.startAccessingSecurityScopedResource() if securityScopeResult { let aliasPathKey = Constants.UserDefaults.aliasPath let aliasPath = UserDefaults.standard.string(forKey: aliasPathKey) let aliasURL = URL(fileURLWithPath: aliasPath) NSWorkspace.shared.open(aliasURL) aliasLocationURL.stopAccessingSecurityScopedResource() } That's pretty much it. The key is to store bookmark data of the location user picked in NSOpenPanel dialog, create symbolic link in it and then you'll be able to access that resource even after app relaunch.
Dec ’22
Reply to How to access files in macOS FileProvider Extension programmatically?
I'm kind of stuck here. I changed code so here is the new approach. I create bookmark in Application Sandboxed Document folder: let url = try! await NSFileProviderManager(for: SREG.context.domain)?.getUserVisibleURL(for: .rootContainer) let bookmarkData = try! url!.bookmarkData(options: [.suitableForBookmarkFile], includingResourceValuesForKeys: nil, relativeTo: nil) let dirURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] let bookmarkURL = dirURL.appendingPathComponent("RootFolderLink") try URL.writeBookmarkData(bookmarkData, to: bookmarkURL) Then at later point when I try to programmatically open the bookmark: let dirURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] let bookmarkURL = dirURL.appendingPathComponent("RootFolderLink") let bookmarkData = try bookmarkURL.bookmarkData(options: .withSecurityScope) let mountURL = try URL(resolvingBookmarkData: bdata, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale) let res = mountURL?.startAccessingSecurityScopedResource() ?? false if res { NSWorkspace.shared.open(mountURL!) mountURL?.stopAccessingSecurityScopedResource() } The Finder does indeed open the FileProvider root folder. However, after quitting the app and trying to open the folder again, I get error message: Important note is that I still have the part with NSOpenPanel where user chooses location where to store bookmark (ie in his Home folder). And only in session where this dialog is shown, opening bookmark will work programmatically. How was app able to open the bookmark link in the same session when it was created, but after app relaunch it refuses to open the link? What can I do to fix this issue? Could it have something to do with signing? This is Xcode debug run.
Dec ’22
Reply to How to access files in macOS FileProvider Extension programmatically?
I have tried playing with bookmark files like: let url = try! await NSFileProviderManager(for: SREG.context.domain)?.getUserVisibleURL(for: .rootContainer) let bookmarkData = try! url!.bookmarkData(options: [.suitableForBookmarkFile], includingResourceValuesForKeys: nil, relativeTo: nil) let savePanel = NSOpenPanel() savePanel.canChooseDirectories = true let result = await savePanel.begin() if result == NSApplication.ModalResponse.OK { var mountUrl = panelURL.appendingPathComponent("MyApp") do { try URL.writeBookmarkData(bookmarkData, to: mountUrl) let bdata = try mountUrl?.bookmarkData(options: .withSecurityScope) UserDefaults.standard.set(bdata!.base64EncodedString(), forKey: "b64") } catch { // print error } } and then later on next app run: bookmarkURL = URL(fileURLWithPath: "/Users/me/MyApp") let b64 = UserDefaults.standard.string(forKey: "b64")! let bdata = Data(base64Encoded: b64) let mountURL = try URL(resolvingBookmarkData: bdata!, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale) let res = mountURL?.startAccessingSecurityScopedResource() if res { NSWorkspace.shared.open(mountURL!) mountURL?.stopAccessingSecurityScopedResource() } but I get the same error that "The application “MyApp” does not have permission to open “MyApp-FileProviderExtension-1".” It does however work on the same app run where bookmark is created and then accessed at later point, but I think it's because NSOpenPanel has been shown in the same app run session. What I'm looking for is a way for a MainApp to open files from FileProviderExtension on user's demand (like user clicking on some button to open root folder or a specific file from list of Uploaded files...)
Nov ’22
Reply to No DistributedNotificationCenter.postNotificationName(_:object:userInfo:deliverImmediately:) method available?
Thank you Eskimo! That was the issue. Now sending notification from FileProviderExtension to the Main app works. Would you recommend using DistributedNotificationCenter for exchainging messages between MainApp and FileProviderExtension or XPC? I find DistributedNotificationCenter so simple to use - just post a notification and receive on other end. But I sense that XPC can be more powerful and more convenient for cases when many messages are sent in short time - ie updating file transfer progress in the Main App UI (FileProviderExtension downloads/uploads a file and wants to update transfer progress to the Main App UI).
Aug ’22
Reply to Can FileProvider provide the full quicklook experience for PDF's?
If a file is downloaded within your File Provider extension, space-bar quicklook preview should already work on PDFs documents. If a file however is not downloaded... then it becomes a bit tricky. You could have your own extension like .myextension for non-downloaded files and then implement your custom Quick Look Preview Extension where you could show ie PDF page images fetched from the server or something like that. But I'm not sure if that's what you're aiming at here.
Jul ’22